package com.zonski.jbots.game;

import com.zonski.jbots.engine.*;

import java.util.Vector;

public class EntityHelper
{
    private EntityHelper()
    {
    }

    /**
     * creates an entity and centres it under its parent
     */
    public static final Entity createEntity(String name, Entity owner, Engine engine, int layerId)
        throws Exception
    {
        EntityFactory entityFactory = engine.getEntityFactory();
        Entity entity = entityFactory.createEntity(name);
        entity.layerId = layerId;
        entity.bounds.setCentre(owner.bounds);
        return entity;
    }

    /**
     * creates an entity, centres it under its parent, and adds it to the
     * current room
     */
    public static final Entity addEntity(String name, Entity owner, Engine engine)
        throws Exception
    {
        return addEntity(name, owner, engine, owner.layerId);
    }

    public static final Entity addEntity(String name, Entity owner, Engine engine, int layerId)
        throws Exception
    {
        Entity entity = createEntity(name, owner, engine, layerId);
        engine.addEntity(entity);
        return entity;
    }

    public static final void centreAtClosestTile(
            Rectangle toCentre, Rectangle bounds, Room room)
    {
        int tileSize = room.getTileSize();
        int tx = getCentreTileX(bounds, tileSize);
        int ty = getCentreTileY(bounds, tileSize);
        toCentre.setCentre(tx * tileSize + tileSize/2,
                ty * tileSize + tileSize/2);
    }

    public static int getCentreTileX(Rectangle bounds, int tileSize)
    {
        int cx = bounds.getCentreX();
        int tx = cx / tileSize;
        return tx;
    }

    public static int getCentreTileY(Rectangle bounds, int tileSize)
    {
        int cy = bounds.getCentreY();
        int ty = cy / tileSize;
        return ty;
    }

    public static boolean isOccupied(Entity entity, Engine engine)
    {
        return isOccupied(entity, engine, -1);
    }

    /**
     * Indicates whether the tile that the entity is on is occupied
     * @param entity the entity that is on the tile, is ignored
     * @param engine the engine being played
     * @return whether there are other objects on the tile centre tile for the entity
     */
    public static boolean isOccupied(Entity entity, Engine engine, int exclude)
    {
        int tileSize = engine.getRoom().getTileSize();
        Rectangle bounds = entity.bounds;
        int tx = EntityHelper.getCentreTileX(bounds, tileSize);
        int ty = EntityHelper.getCentreTileY(bounds, tileSize);
        return isOccupied(tx, ty, entity, engine, exclude);
    }

    public static boolean isOccupied(int tx, int ty, Entity entity, Engine engine, int exclude)
    {
        QuickVector entities = engine.getStateMatrix().getEntitiesOnTile(tx, ty);
        boolean occupied = false;
        if(entities != null)
        {
            for(int i=entities.size(); i>0 && !occupied; )
            {
                i--;
                Entity found = (Entity)entities.elementAt(i);
                if(entity != found &&
                        found.entityType.getId() != JBotsConstants.RECHARGE_ID &&
                        found.entityType.getId() != JBotsConstants.CHARGE_ID &&
                        !JBotsConstants.isBot(found.entityType.getId()) &&
                        exclude != found.entityType.getId())

                {
                    occupied = true;
                }
            }
        }
        return occupied;
    }

    public static int getCharge(Entity entity)
    {
        return entity.getCharge();
    }

    public static void addCharge(Entity entity, int charge, boolean setMaximum)
    {
        int actualCharge = entity.getCharge() + charge;
        if(setMaximum &&  actualCharge > entity.getMaxCharge())
        {
            entity.setMaxCharge(actualCharge);
        }
        entity.setCharge(actualCharge);
    }

    /**
     * Obtains the first entity that is in a different layer in the specified direction
     * @param filter filter for interesting entities
     * @param direction the direction to search in
     * @param engine the engine to search
     * @return all the interesting entities, or null if there were none
     */
    public static QuickVector getInterestingEntities(EntityFilter filter, int tx, int ty, int direction, Engine engine)
    {
        // adjust the size of the laser to fit the free space
        int tileSize = engine.getRoom().getTileSize();
        int tilesAcross = engine.getRoom().getNumberOfTilesAcross();
        int tilesDown = engine.getRoom().getNumberOfTilesDown();
        int dx;
        int dy;
        switch(direction)
        {
            default:
            case Directions.NORTH:
                dx = 0;
                dy = 1;
                break;
            case Directions.SOUTH:
                dx = 0;
                dy = -1;
                break;
            case Directions.WEST:
                dx = -1;
                dy = 0;
                break;
            case Directions.EAST:
                dx = 1;
                dy = 0;
                break;
        }

        QuickVector interesting = null;
        outer: while(tx >=0 && tx < tilesAcross && ty >= 0 && ty < tilesDown)
        {
            QuickVector v = engine.getStateMatrix().getEntitiesOnTile(tx, ty);
            if(v != null)
            {
                for(int i=v.size(); i> 0; )
                {
                    i--;
                    Entity found = (Entity)v.elementAt(i);
                    if(filter.isInteresting(found))
                    {
                        if(interesting == null)
                        {
                            interesting = new QuickVector(5);
                        }
                        interesting.addElement(found);
                    }
                    if(filter.isStopper(found))
                    {
                        break outer;
                    }
                }
            }
            tx += dx;
            ty += dy;
        }
        return interesting;
    }
}
